﻿namespace DotNetCommon.Extensions;

using DotNetCommon;
using System;
using System.Collections;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;

/// <summary>
/// <see cref="IEnumerable{T}"/> 扩展类
/// </summary>
public static class EnumerableExtensions
{
    #region ToConcurrentDictionary
    /// <summary>
    /// 将当前集合对象转成并发字典(属性名->属性值)
    /// </summary>
    public static ConcurrentDictionary<TKey, TValue> ToConcurrentDictionary<T, TKey, TValue>(this IEnumerable<T> collections, Func<T, TKey> keySelector, Func<T, TValue> valueSelector)
    {
        var dic = new ConcurrentDictionary<TKey, TValue>();
        foreach (var collect in collections)
        {
            var key = keySelector(collect);
            var value = valueSelector(collect);
            dic.TryAdd(key, value);
        }
        return dic;
    }
    #endregion

    /// <summary>
    /// 简化 Skip((pageIndex-1) * pageSize).Take(pageSize)调用,页码从1开始
    /// </summary>
    public static IEnumerable<T> GetPage<T>(this IEnumerable<T> sequence, int pageIndex, int pageSize) =>
        sequence.Skip((pageIndex - 1) * pageSize).Take(pageSize);

    /// <summary>
    /// 判断集合中是否存在元素
    /// </summary>
    public static bool IsNotNullOrEmpty<T>(this IEnumerable<T> sequence) =>
        sequence != null && sequence.Any();

    /// <summary>
    /// 判断集合是否为空
    /// </summary>
    public static bool IsNullOrEmpty<T>(this IEnumerable<T> sequence) =>
        sequence == null || (!sequence.Any());

    #region 防止污染, 先不做扩展方法
    /// <summary>
    /// 遍历, 类似 IEnumerable&lt;T>
    /// </summary>
    public static bool Any(IEnumerable sequence, Func<object, bool> predicate)
    {
        AssertUtil.NotNull(sequence);
        AssertUtil.NotNull(predicate);

        foreach (var element in sequence)
        {
            if (predicate(element))
            {
                return true;
            }
        }
        return false;
    }
    #endregion

    /// <summary>
    /// 将当前集合中的元素根据指定的分隔符(<paramref name="separator"/>)拼接成字符串
    /// </summary>
    public static string ToStringSeparated<T>(this IEnumerable<T> sequence, string separator)
    {
        if (sequence == null) return null;
        if (!sequence.Any()) { return string.Empty; }
        return string.Join(separator, sequence);
    }

    /// <summary>
    /// 将当前集合中的元素根据指定的分隔符(<paramref name="delimiter"/>)拼接成字符串
    /// </summary>
    public static string ToCharSeparated<T>(this IEnumerable<T> sequence, char delimiter)
    {
        if (sequence == null) return null;
        if (!sequence.Any()) { return string.Empty; }
        return string.Join(delimiter, sequence);
    }


    /// <summary>
    /// 将当前集合中的元素根据分隔符(<c>,</c>)拼接成字符串
    /// </summary>
    public static string ToCommaSeparated<T>(this IEnumerable<T> sequence) =>
        ToCharSeparated(sequence, ',');

    /// <summary>
    /// 针对集合中的每个元素进行调用
    /// </summary>
    public static void ForEach<T>(this IEnumerable<T> sequence, Action<T> action)
    {
        if (sequence == null) return;
        AssertUtil.NotNull(action);
        foreach (var item in sequence) { action(item); }
    }

    /// <summary>
    /// 从集合中随机选取一个元素
    /// </summary>
    public static T SelectRandom<T>(this IEnumerable<T> sequence)
    {
        AssertUtil.NotNull(sequence);
        var random = new Random(Guid.NewGuid().GetHashCode());
        return sequence.SelectRandom(random);
    }

    /// <summary>
    /// 根据指定的随机数生成器，从当前集合中随机选取一个元素
    /// </summary>
    public static T SelectRandom<T>(this IEnumerable<T> sequence, Random random)
    {
        AssertUtil.NotNull(sequence);
        AssertUtil.NotNull(random);
        if (sequence is ICollection<T> collection)
        {
            return collection.ElementAt(random.Next(collection.Count));
        }

        var count = 1;
        var selected = default(T);

        foreach (var element in sequence)
        {
            if (random.Next(count++) == 0)
            {
                // Select the current element with 1/count probability
                selected = element;
            }
        }
        return selected;
    }

    /// <summary>
    /// 从当前集合中随机选取所有的元素生成新的集合
    /// </summary>
    public static IEnumerable<T> Randomize<T>(this IEnumerable<T> sequence)
    {
        AssertUtil.NotNull(sequence);
        return Randomize(sequence, new Random(Guid.NewGuid().GetHashCode()));
    }

    /// <summary>
    /// 根据指定的随机数生成器，从当前集合中随机选取所有的元素生成新的集合
    /// </summary>
    public static IEnumerable<T> Randomize<T>(this IEnumerable<T> sequence, Random random)
    {
        AssertUtil.NotNull(sequence);
        AssertUtil.NotNull(random);

        var buffer = sequence.ToArray();
        for (var i = 0; i < buffer.Length; i++)
        {
            var j = random.Next(i, buffer.Length);
            yield return buffer[j];

            buffer[j] = buffer[i];
        }
    }

    /// <summary>
    /// 先尝试 as IList&lt;T> 如果失败则调用 ToList&lt;T>()
    /// </summary>
    public static IList<T> SpeculativeToList<T>(this IEnumerable<T> sequence) => sequence as IList<T> ?? sequence.ToList();

    /// <summary>
    /// 先尝试 as IReadOnlyList&lt;T> 如果失败则调用 ToList&lt;T>().AsReadOnly()
    /// </summary>
    public static IReadOnlyList<T> SpeculativeToReadOnlyList<T>(this IEnumerable<T> sequence) => sequence as IReadOnlyList<T> ?? sequence.ToList().AsReadOnly();

    /// <summary>
    /// 先尝试 as T[] 如果失败则调用 ToArray()
    /// </summary>
    public static T[] SpeculativeToArray<T>(this IEnumerable<T> sequence) => sequence as T[] ?? sequence.ToArray();

    /// <summary>
    /// 如果当前不是 IReadOnlyList&lt;T> 则调用 sequence.Skip(0) 变成只读集合
    /// </summary>
    public static IEnumerable<T> ToReadOnlySequence<T>(this IEnumerable<T> sequence)
    {
        AssertUtil.NotNull(sequence);
        return sequence is IReadOnlyList<T> ? sequence : sequence.Skip(0);
    }

    /// <summary>
    /// 当前集合(<c>sequence</c>)是否含有<c>values</c>中的任何一个
    /// <list type="bullet">
    /// <item>当<c>values</c>为空时,直接返回true,否则下一步;</item>
    /// <item>当<c>sequence</c>为空时,直接返回false,否则下一步;</item>
    /// <item>返回<c>sequence</c>和<c>values</c>是否有交叉(Intersect);</item>
    /// </list>
    /// </summary>
    public static bool ContainsAny<T>(this IEnumerable<T> sequence, IEnumerable<T> values)
        => ContainsAny<T>(sequence, values, null);

    /// <summary>
    /// 当前集合(<c>sequence</c>)是否含有<c>values</c>中的任何一个
    /// <list type="bullet">
    /// <item>当<c>values</c>为空时,直接返回true,否则下一步;</item>
    /// <item>当<c>sequence</c>为空时,直接返回false,否则下一步;</item>
    /// <item>返回<c>sequence</c>和<c>values</c>是否有交叉(Intersect);</item>
    /// </list>
    /// </summary>
    public static bool ContainsAny<T>(this IEnumerable<T> sequence, IEnumerable<T> values, IEqualityComparer<T> equalityComparer = null)
    {
        if (values.IsNullOrEmpty()) return true;
        if (sequence.IsNullOrEmpty()) return false;
        return sequence.Intersect(values, equalityComparer).IsNotNullOrEmpty();
    }

    /// <summary>
    /// 当前集合(<c>sequence</c>)是否包含<c>values</c>中的全部
    /// <list type="bullet">
    /// <item>当<c>values</c>为空时,直接返回true,否则下一步;</item>
    /// <item>当<c>sequence</c>为空时,直接返回false,否则下一步;</item>
    /// <item>返回<c>values</c>减去<c>sequence</c>后是否为空(Except);</item>
    /// </list>
    /// </summary>
    public static bool ContainsAll<T>(this IEnumerable<T> sequence, IEnumerable<T> values)
        => ContainsAll<T>(sequence, values, null);

    /// <summary>
    /// 当前集合(<c>sequence</c>)是否包含<c>values</c>中的全部
    /// <list type="bullet">
    /// <item>当<c>values</c>为空时,直接返回true,否则下一步;</item>
    /// <item>当<c>sequence</c>为空时,直接返回false,否则下一步;</item>
    /// <item>返回<c>values</c>减去<c>sequence</c>后是否为空(Except);</item>
    /// </list>
    /// </summary>
    public static bool ContainsAll<T>(this IEnumerable<T> sequence, IEnumerable<T> values, IEqualityComparer<T> equalityComparer = null)
    {
        if (values.IsNullOrEmpty()) return true;
        if (sequence.IsNullOrEmpty()) return false;
        return values.Except(sequence, equalityComparer).IsNullOrEmpty();
    }
}